/***************************************************************************/
/*                                                                         */
/* 234218 Data Structures 1, Spring 2011                                          */
/*                                                                         */
/* Homework : Wet 2                                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* File Name : main2.cpp                                                   */
/*                                                                         */
/* Holds the "int main()" function and the parser of the shell's           */
/* command line.                                                           */
/***************************************************************************/


#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "library2.h"
#include "rational.h"
#include <iomanip>
#include <iostream>
#include <sstream>

using namespace std;
using namespace rational;


/* The command's strings */
typedef enum {
  NONE_CMD = -2,
  COMMENT_CMD = -1,
  INIT_CMD = 0,
  ADDSENSOR_CMD = 1,
  REMOVESENSOR_CMD = 2,
  CHANGESENSORTEMP_CMD = 3,
  GETSENSORTEMP_CMD = 4,
  AVERAGETEMP_CMD = 5,
  MAXTEMP_CMD = 6,
  QUIT_CMD = 7
} commandType;



static const int   numActions   = 8;
static const char *commandStr[] = {
  "Init",
  "AddSensor",
  "RemoveSensor",
  "ChangeSensorTemp",
  "GetSensorTemp",
  "AverageTemp",
  "MaxTemp",
  "Quit"
};


static const char* ReturnValToStr(int val) {
	switch (val) {
		case (SUCCESS):          return "Success";
		case (FAILURE):          return "Failure";
		case (ALLOCATION_ERROR): return "Allocation_error";
		case (INVALID_INPUT):    return "Invalid_input";
		default:                 return "";
	}
}
	



/* we assume maximum string size is not longer than 256  */
#define MAX_STRING_INPUT_SIZE (255)
#define MAX_BUFFER_SIZE       (255)

#define StrCmp(Src1,Src2) ( strncmp((Src1),(Src2),strlen(Src1)) == 0 )

typedef enum {error_free, error} errorType;
static errorType parser(const char* const command);



#define ValidateRead(read_parameters,required_parameters,ErrorString) \
if ( (read_parameters)!=(required_parameters) ) { printf(ErrorString); return error; }


static bool isInit = false;


/* Print an array */
string PrintIntArray(const int* arr, int size) {
	char buffer[MAX_BUFFER_SIZE];
	string str = "";

	for (int i=0; i < size; i++) {
		sprintf(buffer,"%d",arr[i]);
		str += string(buffer) + ((i == (size - 1)) ? "" : ",");
	}
	return str.c_str();
}


/***************************************************************************/
/* main                                                                    */
/***************************************************************************/

int main(int argc, const char**argv) {
	FILE* inputFile = NULL;
	if(argc == 2){
		inputFile = fopen(argv[1], "r");
		if (inputFile == NULL){
			printf("Problem opening file %s, make sure correct path name is given. ", argv[1]);
			return 0;
		}
	}
	else {
	  inputFile = stdin;
	}
	
	char buffer[MAX_STRING_INPUT_SIZE];
	// Reading commands
	while ( fgets(buffer, MAX_STRING_INPUT_SIZE, inputFile) != NULL ) {
	fflush(stdout);
	if ( parser(buffer) == error )
		break;
	};
	fclose(inputFile);
	return 0;
};


/***************************************************************************/
/* Command Checker                                                         */
/***************************************************************************/

static commandType CheckCommand(const char* const command, const char** const command_arg) {
  if ( command == NULL || strlen(command) == 0 || StrCmp("\n", command) )
    return(NONE_CMD);
  if ( StrCmp("#", command) ) {
    if (strlen(command) > 1)
      printf("%s", command);
    return(COMMENT_CMD);
  };
  for (int index=0; index < numActions; index++) {
    if ( StrCmp(commandStr[index], command) ) {
      *command_arg = command + strlen(commandStr[index]) + 1;
      return((commandType)index);
    };
  };
  return(NONE_CMD);
};

/***************************************************************************/
/* Commands Functions                                                      */
/***************************************************************************/

static errorType OnInit(void** DS, const char* const command);
static errorType OnAddSensor(void* DS, const char* const command);
static errorType OnRemoveSensor(void* DS, const char* const command);
static errorType OnChangeSensorTemp(void* DS, const char* const command);
static errorType OnGetSensorTemp(void* DS, const char* const command);
static errorType OnAverageTemp(void* DS, const char* const command);
static errorType OnMaxTemp(void* DS, const char* const command);
static errorType OnQuit(void** DS, const char* const command);


/***************************************************************************/
/* Parser                                                                  */
/***************************************************************************/

static errorType parser(const char* const command) { 
  static void *DS = NULL; /* The general data structure */
  const char* command_args = NULL;
  errorType rtn_val = error;

  commandType command_val = CheckCommand(command, &command_args);
 
  switch (command_val) {

	case (INIT_CMD):                rtn_val = OnInit(&DS, command_args);				break;
	case (ADDSENSOR_CMD):           rtn_val = OnAddSensor(DS, command_args);			break;
	case (REMOVESENSOR_CMD):        rtn_val = OnRemoveSensor(DS, command_args);			break;
	case (CHANGESENSORTEMP_CMD):    rtn_val = OnChangeSensorTemp(DS, command_args);		break;
	case (GETSENSORTEMP_CMD):       rtn_val = OnGetSensorTemp(DS, command_args);		break;
	case (AVERAGETEMP_CMD):     	rtn_val = OnAverageTemp(DS, command_args);			break;
	case (MAXTEMP_CMD):				rtn_val = OnMaxTemp(DS, command_args);				break;
	case (QUIT_CMD):                rtn_val = OnQuit(&DS, command_args);				break;
	
	case (COMMENT_CMD):             rtn_val = error_free;								break;
	case (NONE_CMD):                rtn_val = error;									break;
	default: assert(false);
  };
  return(rtn_val);
};





/***************************************************************************/
/* OnInit                                                                  */
/***************************************************************************/
static errorType OnInit(void** DS, const char* const command) {
	if(isInit) {
		printf("Init was already called.\n");
		return(error_free);
	};
	isInit = true;

	*DS = Init();
	if( *DS == NULL ) {
		printf("Init failed.\n");
		return(error);
	};
	printf("Init done.\n");
	return error_free;
}


/***************************************************************************/
/* OnAddSensor                                                             */
/***************************************************************************/
static errorType OnAddSensor(void* DS, const char* const command) {
	int id;
	int lat[2];
	int t[2];
	ValidateRead( sscanf(command, "%d,%d.%d,%d.%d",&id,&lat[0],&lat[1],&t[0], &t[1]), 5, "AddSensor failed.\n" );
	if ( lat[1] >= 10 || lat[1] < 0 || t[1] >= 10 || t[1] < 0) {
		printf("Updates should be written to 1 decimal places."); return error;
	}
	Rational latitude((long long)lat[0] * 10 + lat[1], (long long)10);
	Rational temp((long long)t[0] * 10 + t[1], (long long)10);
	StatusType res = AddSensor(DS,id, latitude, temp);

	printf("AddSensor: %s\n", ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnRemoveSensor                                                     	   */
/***************************************************************************/
static errorType OnRemoveSensor(void* DS, const char* const command) {
	int id;
	ValidateRead( sscanf(command, "%d",&id), 1, "RemoveSensor failed.\n" );
	
	StatusType res = RemoveSensor(DS, id);

	printf("RemoveSensor: %s\n", ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnChangeSensorTemp                                                      */
/***************************************************************************/
static errorType OnChangeSensorTemp(void* DS, const char* const command) {

	int id;
	int t[2];
	ValidateRead( sscanf(command, "%d,%d.%d",&id, &t[0], &t[1]), 3, "ChangeSensorTemp failed.\n" );
	if (  t[1] >= 10 || t[1] < 0) {
		printf("Updates should be written to 1 decimal places."); return error;
	}
	Rational temp((long long)t[0] * 10 + t[1], (long long)10);
	StatusType res = ChangeSensorTemp(DS,id,temp);
	
	printf("ChangeSensorTemp: %s\n",ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnGetSensorTemp                                                         */
/***************************************************************************/
static errorType OnGetSensorTemp(void* DS, const char* const command) {
	int id;
	ValidateRead( sscanf(command, "%d",&id), 1, "GetSensorTemp failed.\n" );

	Rational temp;
	StatusType res = GetSensorTemp(DS,id,&temp);
	
	if (res != SUCCESS) {
		printf("GetSensorTemp: %s\n",ReturnValToStr(res));
	}
	else {
		std::ostringstream stream;
		stream << setprecision(2) << temp;
		std::string str =  stream.str();
		printf("GetSensorTemp: %s %s\n", ReturnValToStr(res),str.c_str());
	}

	return error_free;
}


/***************************************************************************/
/* OnAverageTemp   			                                               */
/***************************************************************************/
static errorType OnAverageTemp(void* DS, const char* const command) {
	int	lat1[2];
	int	lat2[2];
	ValidateRead( sscanf(command, "%d.%d,%d.%d",&lat1[0],&lat1[1],&lat2[0],&lat2[1]), 4, "AverageTemp failed.\n" );
	if ( lat1[1] >= 10 || lat1[1] < 0 || lat2[1] >= 10 || lat2[1] < 0) {
		printf("Updates should be written to 1 decimal places."); return error;
	}
	Rational latitude1((long long)lat1[0] * 10 + lat1[1], (long long)10);
	Rational latitude2((long long)lat2[0] * 10 + lat2[1], (long long)10);
	Rational temp;
	StatusType res = AverageTemp(DS,latitude1, latitude2,&temp);
	
	if (res != SUCCESS) {
		printf("AverageTemp: %s\n",ReturnValToStr(res));
	}
	else {
		std::ostringstream stream;
		stream << setprecision(2) << temp;
		std::string str =  stream.str();
		printf("AverageTemp: %s, %s\n", ReturnValToStr(res),str.c_str());
	}

	return error_free;
}


/***************************************************************************/
/* OnMaxTemp					                                           */
/***************************************************************************/
static errorType OnMaxTemp(void* DS, const char* const command) {
	int	lat1[2];
	int	lat2[2];
	ValidateRead( sscanf(command, "%d.%d,%d.%d",&lat1[0],&lat1[1],&lat2[0],&lat2[1]), 4, "MaxTemp failed.\n" );
	if ( lat1[1] >= 10 || lat1[1] < 0 || lat2[1] >= 10 || lat2[1] < 0) {
		printf("Updates should be written to 1 decimal places."); return error;
	}
	Rational latitude1((long long)lat1[0] * 10 + lat1[1], (long long)10);
	Rational latitude2((long long)lat2[0] * 10 + lat2[1], (long long)10);
	Rational temp;
	StatusType res = MaxTemp(DS,latitude1, latitude2,&temp);
	
	if (res != SUCCESS) {
		printf("MaxTemp: %s\n",ReturnValToStr(res));
	}
	else {
		std::ostringstream stream;
		stream << setprecision(2) << temp;
		std::string str =  stream.str();
		printf("MaxTemp: %s, %s\n", ReturnValToStr(res),str.c_str());
	}

	return error_free;
}

/***************************************************************************/
/* OnQuit                                                                  */
/***************************************************************************/
static errorType OnQuit(void** DS, const char* const command) {
	Quit(DS);
	if( *DS != NULL ) {
		printf("Quit failed.\n");
		return(error);
	};
	isInit = false;
	printf("Quit done.\n");

	return error_free;
}

